SPDX-FileCopyrightText: 2019 Gabriel Châtel & Charles Preham SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be
SPDX-License-Identifier: GPL-3.0-or-later
Blender 2.80 _ Preham _ OSX
BRIAN ENO - Ambient #1 MFA: # formal interpretation by # procedural modeling # # Authors: [C.PREHAM/G.CHÂTEL] # Date: [24.01.2019] # Blender version: [2.8 & hash] # OS: [MacOS 10.13.6] #
external functions importation :
import bpy
import random
import math
from math import radians
from math import cos
from math import sinlast run supression :
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)shortcuts :
cylinder = bpy.ops.mesh.primitive_cylinder_add
cube = bpy.ops.mesh.primitive_cube_addfunction definition :
def smoothhigh(
high,
): # function that smooth the height of the next note (next value randomly take +1 or -1)
highloop = [high - 1, high + 1]
high = random.choice(highloop)
if high <= 1: # bottom limit
high = 2
if high >= 5: # top limit
high = 4
return highdef smoothradius(
radius,
): # function that smooth the radius of the next note (next value randomly take +.1 or -.1)
radiusloop = [radius - 0.1, radius + 0.1]
radius = random.choice(radiusloop)
if radius <= 0.1: # inner limit
radius = 0.2
if radius >= 0.8: # outer limit
radius = 0.7
return radiusdef score(
x, y, high, radius
): # function that make the score (notes (cylinders), lines of notes, ground (floor) and roof (squares and wireframed squares))
res = [] # list of all lines of notes
for _ in range(0, squaresize):
line = [] # list of all notes in a line
for _ in range(0, squaresize):
radius = smoothradius(radius)
high = smoothhigh(high)
highchoice = [
high / 2,
high / 2,
(10 - high / 2),
] # choice of the position of the column (2/3 on bottom, 1/3 on top)
z = random.choice(highchoice)
bpy.ops.surface.primitive_nurbs_surface_cylinder_add(
radius=radius, enter_editmode=False, location=(x, y, z)
)
bpy.context.object.scale[2] = high / (2 * radius)
line.append(bpy.context.selected_objects[0])
if x > 5: # changing some circles into ellipses based on ?????
bpy.context.object.scale[0] = 0.7
if y < 5:
bpy.context.object.scale[1] = 0.7
roof(x, y, radius, high, z) # function that make the roof
x += 2
res.append(line)
x = 1
y += 2
return resdef zone(
matrice, xindex, yindex
): # function that make matrices off all 8 (or 5 or 3) neighbours columns of each single column
for xindex in range(len(matrice[0])):
for yindex in range(len(matrice[0])):
if (
xindex >= 1
and xindex <= (squaresize - 2)
and yindex >= 1
and yindex <= (squaresize - 2)
):
neighbour(xindex, yindex)def neighbour(
xindex, yindex
): # function that make a list of all 8 (or 5 or 3) neighbours columns + the middle one. . . . . . . . . . • o . . . . o o o . o o . . . o o . . . . o • o . 8 neighbours • o . . . 5 neighbours . . . . . 3 neighbours . o o o . o o . . . . . . . . . . . . . . . . . . . . . . .
neighbourlist = []
neighbourlist.append(matrice[xindex - 1][yindex + 1])
neighbourlist.append(matrice[xindex][yindex + 1])
neighbourlist.append(matrice[xindex + 1][yindex + 1])
neighbourlist.append(matrice[xindex - 1][yindex])
neighbourlist.append(matrice[xindex][yindex])
neighbourlist.append(matrice[xindex + 1][yindex])
neighbourlist.append(matrice[xindex - 1][yindex - 1])
neighbourlist.append(matrice[xindex][yindex - 1])
neighbourlist.append(matrice[xindex + 1][yindex - 1])
print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
print(" ")
print("list of columns for checking parameters :")
print(neighbourlist)
events(neighbourlist, xindex, yindex, dcu)def coord(
p1, p2, i, inc, z, name, x, y
): # function that make a list of 2 points (one above the other) for buildLines ()(point1, point2, index position, incrementation,…)
(p1, p2) = ([x, y], [x, y])
p1[i] += inc
p1.append(z) # adding z coord to first point
p2[i] += inc
p2.append(z + 10) # adding z coord to second point
pointXYZ = [p1, p2]
print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
print(" ")
print("list of two coordinates for buildLines () :")
print(pointXYZ)
if x >= 5: # condition for waves
buildLines(
pointXYZ, name, 0
) # function that make a wave between the two points
elif x < 5: # condition for 90° waves
global p # count to know the exact index of each part of wave
p += 1
buildLines(
pointXYZ, name, p
) # function that make a wave between the two points (90°)def events(neighbourlist, xindex, yindex, dcu): # function that make append the events
zcu = 0
rcu = 0
for obj in neighbourlist:
name = obj.name
point = [bpy.data.objects[name].location[0], bpy.data.objects[name].location[1]]
pointlist.append(point)
highcu = bpy.data.objects[name].location[2]
zcu += highcu # cumalated height
radcu = bpy.data.objects[name].dimensions[1] / 2
rcu += radcu # cumalated radius
denscu = bpy.data.objects[name].dimensions[0]
dcu = denscu # cumulated density
print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
print(" ")
print("zcu=") and print(zcu)
print("rcu=") and print(rcu)
print("dcu=") and print(dcu)
ground(xindex, yindex, dcu)
if rcu < 4: # condition of the first event
stairs_wall()
if zcu >= 40: # condition of the second event
[x, y] = random.choice(pointlist)
if x >= 5:
coord(p1, p2, 0, 0, z, "Halfcircle", x, y)
coord(p1, p2, 0, 2, z, "Halfcircle2", x, y)
[x, y] = random.choice(pointlist)
if x < 5:
coord(p1, p2, 0, 0, z, "Halfcircle", x, y)
coord(p1, p2, 1, 2, z, "Halfcircle2", x, y)def buildLines(points, ID, p): # function that connect points into single linescredit to Michel & Julien for this part of the code
all = bpy.ops.object.select_all
for p1, p2 in zip(points, points[1:]):for each point couple (n, n+1)
create the Curve Datablock
curveData = bpy.data.curves.new("myCurves", type="CURVE")
curveData.dimensions = "3D"
curveData.resolution_u = 2map coords to spline
polyline = curveData.splines.new("POLY")
polyline.points.add(1)
for i, coord in enumerate([p1, p2]):
x, y, z = coord
polyline.points[i].co = (x, y, z, 1)create object
curveOB = bpy.data.objects.new("myCurves", curveData)apply custom section
bpy.ops.object.select_all(action="DESELECT")
curveData.bevel_object = bpy.data.objects[ID] # custom object
curveData.use_fill_caps = Trueattach to scene and validate context
scn = bpy.context.scene
scn.collection.objects.link(curveOB)
if p < 1:
rotate_wave("myCurves", p)
if 1 <= p <= 9:
rotate_wave("myCurves.00", p)
if p > 9:
rotate_wave("myCurves.0", p)def rotate_wave(curve, p):function that rotate the waves
if p < 1:
name = curve
if p != 0:
name = curve + str(p)
bpy.data.objects[name].select_set(True)
bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")
bpy.ops.transform.rotate(
value=1.5708,
orient_axis="Z",
orient_type="GLOBAL",
orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
orient_matrix_type="GLOBAL",
constraint_axis=(False, False, True),
mirror=True,
use_proportional_edit=False,
proportional_edit_falloff="SMOOTH",
proportional_size=1,
use_proportional_connected=False,
use_proportional_projected=False,
release_confirm=True,
)def stairs_wall():function that randomly generate “stairs walls” from outside to inside
x = random.randrange(0, squaresize * 2, 2)
y = random.randrange(1, squaresize * 2, 2)
z = random.choice([1, 9])
if x <= 5 and y <= 5: # 10x10 square is divided by 4 to keep walls inside the model
for i in range(1, 5):
walls(x, y, z, i)
x += 1
y += 1
if x > 5 and y <= 5:
for i in range(1, 5):
walls(x, y, z, i)
x -= 1
y += 1
elif x <= 5 and y > 5:
for i in range(1, 5):
walls(x, y, z, i)
x += 1
y -= 1
elif x > 5 and y > 5:
for i in range(1, 5):
walls(x, y, z, i)
x -= 1
y -= 1def walls(x, y, z, i):function that generate a single wall for one stair
i = i % 2 # change i to 0 or 1
bpy.ops.mesh.primitive_plane_add(size=2, enter_editmode=False, location=(x, y, z))
bpy.context.object.rotation_euler[i] = -1.5708
fluct(z, 1 - i)def fluct(z, i):function that variate the height of each wall
var = random.randint(20, 35) / 20
bpy.context.object.scale[i] = var
if z == 1:
bpy.context.object.location[2] = var
if z == 9:
bpy.context.object.location[2] = 10 - vardef ground(xindex, yindex, dcu):
global limitground
limitground = 0
limitground += 1
var2 = random.randint(10, 20) / 10
if limitground == 1:
floor(xindex, yindex, dcu, var2)
if limitground == 3:
floor(xindex, yindex, dcu, var2)
if limitground == 7:
floor(xindex, yindex, dcu, var2)
if limitground == 9:
floor(xindex, yindex, dcu, var2)
return zcudef floor(xindex, yindex, dcu, var2):function that make a part of the ground based on the cumulated radius of neighbourlist
cube(size=1, enter_editmode=False, location=(1 + xindex * 2, 1 + yindex * 2, 10))
bpy.context.object.scale[0] = 6
bpy.context.object.scale[1] = 6
bpy.context.object.scale[2] = (dcu - 20) * 0.05 * var2
bpy.context.object.location[2] = -(dcu - 20) * 0.05 * var2 / 2def roof(xindex, yindex, zcu, high, z):
cube(size=1, enter_editmode=False, location=(xindex, yindex, 10 - zcu / 2))
bpy.context.object.scale[0] = 2
bpy.context.object.scale[1] = 2
bpy.context.object.scale[2] = zcu
if z < 5: # columnless roof squares
bpy.ops.object.modifier_add(type="WIREFRAME")
bpy.context.object.modifiers["Wireframe"].thickness = 0.1
bpy.context.object.modifiers["Wireframe"].offset = -1def nurbscurve():
bpy.ops.curve.primitive_nurbs_curve_add(
radius=1, enter_editmode=False, location=(0, 0, 0)
)
bpy.data.objects["NurbsCurve"].scale[1] = 7.999
bpy.data.objects["NurbsCurve"].scale[0] = 1.334
bpy.context.scene.cursor.location = (0.0, 6.67, 0.0)
bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
for obj in bpy.context.selected_objects:
obj.name = "Halfcircle"
bpy.data.objects["Halfcircle"].location[0] = 5
bpy.data.objects["Halfcircle"].location[1] = 5
bpy.ops.curve.primitive_nurbs_circle_add(enter_editmode=False, location=(5, 5, 0))
bpy.context.object.scale[0] = 0.2
bpy.context.object.scale[1] = 0.2
for obj in bpy.context.selected_objects:
obj.name = "Circle"
bpy.ops.curve.primitive_nurbs_curve_add(
radius=1, enter_editmode=False, location=(0, 0, 0)
)
bpy.data.objects["NurbsCurve"].scale[1] = -7.999
bpy.data.objects["NurbsCurve"].scale[0] = 1.334
bpy.context.scene.cursor.location = (0.0, -6.67, 0.0)
bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
for obj in bpy.context.selected_objects:
obj.name = "Halfcircle2"
bpy.data.objects["Halfcircle2"].location[0] = 5
bpy.data.objects["Halfcircle2"].location[1] = 5parameters :
squaresize = 5 # number of squares by line
radius = random.randint(1, 20) / 10
high = random.randint(1, 6)
(x, y, z) = (1, 1, 0)
(xindex, yindex) = (0, 0)
(zcu, rcu, dcu) = (0, 0, 0)
p = -1 # count for rotated waveslists :
curvepoint = []
matrice = []
neighbourlist = []
pointlist = []
pointXYZ = []
(p1, p2) = ([], [])execution :
nurbscurve()
matrice = score(x, y, high, radius)
zone(matrice, xindex, yindex)definition de la fonction de création de camera en projection parallele
def axoCam(projection, canon):
bpy.ops.object.camera_add()
maScene = bpy.context.scene.render
monAxoCam = bpy.context.object
monAxoCam.data.type = "ORTHO"
monAxoCam.data.ortho_scale = 10
if projection == "axonometrique":
if canon == "isometrie": # OK
monAxoCam.name = "axoIsometrie"
monAxoCam.rotation_euler = (radians(54.74), 0.0, radians(45))
monAxoCam.location = (20.0, -10.0, 20.0)
maScene.pixel_aspect_x = 1
if canon == "dimetrie": # OK
monAxoCam.name = "axoDimetrie"
monAxoCam.rotation_euler = (radians(60), 0.0, radians(23))
monAxoCam.location = (5.53, -13.04, 8.18)
maScene.pixel_aspect_x = 1
if canon == "trimetrie": # OK
monAxoCam.name = "axoTrimetrie"
monAxoCam.rotation_euler = (radians(67), 0.0, radians(34))
monAxoCam.location = (8.59, -12.734, 6.52)
maScene.pixel_aspect_x = 1
elif projection == "oblique":
if canon == "militaire": # OK
monAxoCam.name = "oblMilitaire"
monAxoCam.rotation_euler = (radians(45), 0.0, radians(30))
monAxoCam.location = (7.071, -12.247, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45))
if canon == "militaireDiminuee": # OK
monAxoCam.name = "oblMilitaireDiminuee"
monAxoCam.rotation_euler = (radians(45 * 0.82), 0.0, radians(30))
monAxoCam.location = (5.309, -9.195, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45 * 0.82))
if canon == "cavalierePlan": # OK
monAxoCam.name = "oblCavalierePlan"
monAxoCam.rotation_euler = (radians(45), 0.0, radians(45))
monAxoCam.location = (10.0, -10.0, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45))
if canon == "cavalierePlanDiminuee": # OK
monAxoCam.name = "oblCavalierePlanDiminuee"
monAxoCam.rotation_euler = (radians(45 * 0.82), 0.0, radians(45))
monAxoCam.location = (7.5, -7.5, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45 * 0.82))
if canon == "cavaliereAngleCam": # OK mais à recentrer manuellement
angleZ = radians(30) # entrer l'angle de rotation de la camera en Z
angleZ_XY = radians(
15
) # entrer l'angle d'inclinaison de la camera par rapport au sol
monAxoCam.name = "oblCavalierePlanDiminuee"
monAxoCam.rotation_euler = (angleZ_XY, 0.0, angleZ)
monAxoCam.location = (10, -10, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(angleZ_XY)Execution de la fonction de création de la camera choix du type de projection: Effacer le # devant la ligne pour choisir le type de camera à créer
PROJECTION OBLIQUE#
axoCam (‘oblique’,’militaire’) axoCam (‘oblique’,’militaireDiminuee’) axoCam (‘oblique’,’cavalierePlan’) axoCam (‘oblique’,’cavalierePlanDiminuee’) axoCam (‘oblique’,’cavaliereAngleCam’) #celle ci peut être réglée en angle de vue -> regarder dans la dernière formule
PROJECTION AXONOMETRIQUE#
axoCam("axonometrique", "isometrie")axoCam (‘axonometrique’,’dimetrie’) axoCam (‘axonometrique’,’trimetrie’)